using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;

namespace PBR.Render
{
    /// <summary>
    /// Handles painting invidual brush strokes onto an image
    /// </summary>
    public class Painter
    {
        #region Static Variables

        private const float BASELINELENGTH = 100.0f;
        private const float EDGELINELENGTH = 10.0f;
        private const int BASENUMITER = 5000;
        private const int EDGENUMITER = 0;
        private const float BRUSHDELTA = 0.0f;
        private const float ANGLEDELTA = 0.0f;
        private const float ALPHADELTA = 0.0f;
        private const int PLACEMENTDELTA = 10;
        private const float BASEALPHA = 0.9f;
        private const int SOBELBLURSIZE = 21;
        private const double SOBELBLURSIGMA = 5.0;
        private const int COLORBLURSIZE = 5;
        private const double COLORBLURSIGMA = 1.4;
        private const int EDGETHRESHABS = 100;
        private const int EDGETHRESHREL = 50;
        private const bool compositeEdges = false;

        #endregion

        #region Member Variables

        private Scene scene;
        private Bitmap colorBrush;
        private Bitmap heightBrush;
        private Bitmap[] brushes;
        private BrushMipMap colorStraightBrushMip;
        private BrushMipMap colorUpBrushMip;
        private BrushMipMap colorDownBrushMip;
        private BrushMipMap heightBrushMip;
        private RadialBasisInterp radialBasisInterp = new RadialBasisInterp();
        private bool colorBlurDirty = true;

        private float baseLineLength = BASELINELENGTH;
        private float edgeLineLength = EDGELINELENGTH;
        private int baseNumIter = BASENUMITER;
        private int edgeNumIter = EDGENUMITER;
        private float brushDelta = BRUSHDELTA;
        private float angleDelta = ANGLEDELTA;
        private float alphaDelta = ALPHADELTA;
        private int placementDelta = PLACEMENTDELTA;
        private float baseAlpha = BASEALPHA;
        private int sobelBlurSize = SOBELBLURSIZE;
        private double sobelBlurSigma = SOBELBLURSIGMA;
        private int colorBlurSize = COLORBLURSIZE;
        private double colorBlurSigma = COLORBLURSIGMA;
        private int edgeThreshAbs = EDGETHRESHABS;
        private int edgeThreshRel = EDGETHRESHREL;

        private bool edgesDirty = false;

        //private DataSet dataSet;

        private Random rand = new Random();

        /// <summary>
        /// The number of pixels that strokes cross boundaries
        /// </summary>
        public int Overlap = 0;

        #endregion

        #region Member Functions

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="scene"></param>
        public Painter(Scene scene)
        {
            this.scene = scene;

            //dataSet = new DataSet("Paint Properties");

            //DataTable dataTable = new DataTable();
            //dataSet.Tables.Add(dataTable);
        }

        /// <summary>
        /// Composites the segmentation image with the canny edge images
        /// </summary>
        public void CompositeEdges()
        {
            
            // Sobel edge filteor
            //AForge.Imaging.Filters.CannyEdgeDetector cannyEdgeDetector = new AForge.Imaging.Filters.CannyEdgeDetector();
            //Bitmap edgesImage = cannyEdgeDetector.Apply(scene.Painting.ActiveLayer.Color.Bitmap);
            
            // dispose of old images
            //scene.Painting.ActiveLayer.EdgesImage.Dispose();
            //scene.Painting.ActiveLayer.AnglesImage.Dispose();

            //PBR.Filters.SobelEdgeDetectorRotation sobelEdgeFilter = new PBR.Filters.SobelEdgeDetectorRotation();
            //Bitmap edgesImage, anglesImage;
            //sobelEdgeFilter.Apply(scene.Painting.ActiveLayer.ColorImage.Bitmap,
            //    out edgesImage, out anglesImage, scene.Painting.ActiveLayer.EdgePixels, edgeThreshRel);

            // Convert anglesImage and edgesImage into bitmaps
            //AForge.Imaging.Filters.GrayscaleBT709 grayscaleFilter = new AForge.Imaging.Filters.GrayscaleBT709();
            //scene.Painting.ActiveLayer.EdgesImage.Bitmap = grayscaleFilter.Apply(edgesImage);
            //scene.Painting.ActiveLayer.AnglesImage.Bitmap = grayscaleFilter.Apply(anglesImage);

            //AForge.Imaging.Filters.AdaptiveSmooth adaptiveSmooth = new AForge.Imaging.Filters.AdaptiveSmooth();
            //adaptiveSmooth.ApplyInPlace(edgesImage);

            // Generate 

            ////Bitmap workingImage = new Bitmap(scene.Painting.ActiveLayer.SegmentsImage.Bitmap);
            //if (compositeEdges)
            //{
            //    foreach (BitmapUnsafe edgeImage in scene.Painting.ActiveLayer.ImageTier.EdgesImages)
            //    {
            //        Bitmap compositeImage = new Bitmap(scene.Painting.ActiveLayer.SegmentsImage.Bitmap);

            //        // genenerate the vectorDisplay image - alpha matted segments image
            //        PBR.Filters.AlphaMatteFromColor alphaMatteFilter = new PBR.Filters.AlphaMatteFromColor();
            //        alphaMatteFilter.ApplyInPlace(scene.Painting.ActiveLayer.SegmentsImage.Bitmap, compositeImage);

            //        PBR.Filters.Composite composite = new PBR.Filters.Composite();
            //        composite.ApplyInPlace(edgeImage.Bitmap, compositeImage);
            //    }
            //}
            // Blur
            //AForge.Imaging.Filters.GaussianBlur gaussianBlur = new AForge.Imaging.Filters.GaussianBlur(sobelBlurSigma, sobelBlurSize);
            //gaussianBlur.ApplyInPlace(scene.Painting.ActiveLayer.EdgesImage.Bitmap);
            //gaussianBlur.ApplyInPlace(scene.Painting.ActiveLayer.EdgesImage.Bitmap);
            //gaussianBlur.ApplyInPlace(scene.Painting.ActiveLayer.EdgesImage.Bitmap);

            edgesDirty = false;
        }

        /// <summary>
        /// Generate the blurred color image to paint with
        /// </summary>
        public void GenerateBlurredColor()
        {
            scene.Painting.ActiveLayer.BlurredColorImage.Bitmap.Dispose();

            AForge.Imaging.Filters.GaussianBlur gaussianBlur = new AForge.Imaging.Filters.GaussianBlur(colorBlurSigma, colorBlurSize);
            scene.Painting.ActiveLayer.BlurredColorImage.Bitmap = gaussianBlur.Apply(scene.Painting.ActiveLayer.ColorImage.Bitmap);

            colorBlurDirty = false;
        }

        /// <summary>
        /// Takes the current edgesImage and segments it using a connnected component algorithm
        /// </summary>
        public void SegmentImage()
        {
            // TODO
            // These filters could all be put into one filter
            GenerateVectorDisplayImage();

            // temporary copy of our image
            Bitmap workingImage = scene.Painting.ActiveLayer.SegmentsImage.Bitmap;
            
            // grayscale - already done on image load
            AForge.Imaging.Filters.GrayscaleBT709 grayscaleFilter = new AForge.Imaging.Filters.GrayscaleBT709();
            workingImage = grayscaleFilter.Apply(workingImage);

            // invert
            AForge.Imaging.Filters.Invert invertFilter = new AForge.Imaging.Filters.Invert();
            invertFilter.ApplyInPlace(workingImage);

            // threshold
            AForge.Imaging.Filters.Threshold threshold = new AForge.Imaging.Filters.Threshold(128);
            threshold.ApplyInPlace(workingImage);

            // connected component image
            AForge.Imaging.Filters.ConnectedComponentsLabeling connectedComponentsLabeling = new AForge.Imaging.Filters.ConnectedComponentsLabeling();
            scene.Painting.ActiveLayer.ConnectedComponentsImage = connectedComponentsLabeling.Apply(workingImage);

            AForge.Imaging.BlobCounter blobCounter = connectedComponentsLabeling.BlobCounter;

            ArrayList segments = scene.Painting.ActiveLayer.segments;
            segments.Clear();

            Rectangle[] objectRectangles = blobCounter.GetObjectRectangles();

            Material defaultMat = scene.Painting.defaultMaterial;
            for (int i = 0; i < blobCounter.ObjectsCount; i++)
            {
                segments.Add(new Segment(objectRectangles[i], defaultMat));
            }

            int [] objectLables1D = blobCounter.ObjectLabels;
            int[,] objectLabels = new int[workingImage.Width, workingImage.Height];
            int[] segmentsPixelCount = new int[segments.Count];
            // zero the array (not necessary)
            for (int i = 0; i < segmentsPixelCount.Length; i++)
                segmentsPixelCount[i] = 0;

            Segment seg;
            for (int i = 0; i < workingImage.Width; i++)
            {
                for (int j = 0; j < workingImage.Height; j++)
                {
                    objectLabels[i, j] = objectLables1D[j * workingImage.Width + i] - 1;
                    if (objectLabels[i, j] < 0)
                        continue;

                    seg = (Segment)(segments[(objectLabels[i, j])]);
                    seg.Pixels.Add(new Segment.SimplePoint(i, j));
                }
            }
            scene.Painting.ActiveLayer.ObjectLabels = objectLabels;
        }

        public void GenerateVectorDisplayImage()
        {
            scene.Painting.ActiveLayer.OverlayImage.Dispose();
            scene.Painting.ActiveLayer.OverlayImage = new Bitmap(scene.Painting.ActiveLayer.SegmentsImage.Bitmap);

            // grayscale
            //scene.Painting.ActiveLayer.SegmentsImage.Bitmap = grayscaleFilter.Apply(scene.Painting.ActiveLayer.SegmentsImage.Bitmap);

            // generate the vector disply image
            // erode the image - make the edges smaller
            AForge.Imaging.Filters.Erosion erosionFilter = new AForge.Imaging.Filters.Erosion();
            erosionFilter.ApplyInPlace(scene.Painting.ActiveLayer.OverlayImage);

            AForge.Imaging.Filters.GrayscaleBT709 grayscaleFilter = new AForge.Imaging.Filters.GrayscaleBT709();
            Bitmap vectorDisplayGrayscale = grayscaleFilter.Apply(scene.Painting.ActiveLayer.OverlayImage);

            // genenerate the vectorDisplay image - alpha matted segments image
            PBR.Filters.AlphaMatteFromColor alphaMatteFilter = new PBR.Filters.AlphaMatteFromColor();
            alphaMatteFilter.ApplyInPlace(vectorDisplayGrayscale, scene.Painting.ActiveLayer.OverlayImage);
        }

        /// <summary>
        /// Wrapper function which calls the random or saved paint functions
        /// </summary>
        /// <param name="gColor"></param>
        /// <param name="gHeight"></param>
        /// <param name="sourceRect"></param>
        /// <param name="generateAll"></param>
        /// <param name="compositingQuality"></param>
        /// <param name="showBorders"></param>
        public void Paint(Graphics gColor, Graphics gHeight, Rectangle sourceRect, bool generateAll, CompositingQuality compositingQuality, bool showBorders)
        {
            if (scene.Painting.IsRandom)
            {
                PaintRandom(gColor, gHeight, sourceRect, generateAll, compositingQuality, showBorders);
            }
            else
            {
                //PaintSaved(gColor, gHeight, sourceRect, generateAll, compositingQuality, showBorders);
            }
        }

        /// <summary>
        /// Paints the image using the specified graphics
        /// </summary>
        /// <param name="gColor"></param>
        /// <param name="gHeight"></param>
        /// <param name="sourceRect"></param>
        /// <param name="generateHeight"></param>
        /// <param name="compositingQuality"></param>
        /// <param name="showBorders"></param>
        public void PaintRandom(Graphics gColor, Graphics gHeight, Rectangle sourceRect, bool generateAll, CompositingQuality compositingQuality, bool showBorders)
        {
            // Create local references to global resources
            Bitmap colorImage = scene.Painting.ActiveLayer.ColorImage.Bitmap;
            Rectangle fullImageRect = new Rectangle(0, 0, colorImage.Width, colorImage.Height);
            
            // validate input
            if (generateAll && sourceRect != fullImageRect)
            {
                throw new ArgumentException("generateHeight should be true only when paint is called on the entire image");
            }

            //if ( colorBlurDirty )   // Regenerate blurred color if needed
            //{
            //    GenerateBlurredColor();
            //}
            if (scene.Painting.ActiveLayer.segments.Count == 0)
            {
                SegmentImage();
            }

            // Create local references and 
            // clear the old saved stroke data
            List<Stroke> strokeList = scene.Painting.StrokeList;
            if (generateAll)
            {
                strokeList.Clear();
                scene.Painting.StrokeKDTree = new KDTreeDLL.KDTree(2);
            }
            KDTreeDLL.KDTree strokeKDTree = scene.Painting.StrokeKDTree;

            // Decide the number of iterations
            int iterations = (int) (baseNumIter *
                ((double)sourceRect.Width / scene.Painting.ActiveLayer.ColorImage.Width) *
                ((double)sourceRect.Height / scene.Painting.ActiveLayer.ColorImage.Height));
            int numTries = 0;
            int maxTries = iterations * 10;
            //int brushLengthThresh = (int)(baseLineLength / 3); // Don't draw strokes with length less than this

            // Reset the progressBar
            scene.ProgressBar.Value = 0;

            // Set the compositing Modes
            gColor.CompositingMode = CompositingMode.SourceOver;
            gColor.CompositingQuality = compositingQuality;
            gColor.InterpolationMode = InterpolationMode.Bilinear;

            BitmapUnsafe blurredImage;
            BitmapUnsafe edgesImage;
            BitmapUnsafe anglesImage = scene.Painting.ActiveLayer.AnglesImage;
            int[,] objectLabels = scene.Painting.ActiveLayer.ObjectLabels;
            //BitmapUnsafe colorImage = scene.Painting.ActiveLayer.Color;
            KDTreeDLL.KDTree vectorsKDTree;
            BrushMipMap colorBrushMip;
            BrushMipMap heightBrushMip;
            ArrayList edgePixels = scene.Painting.ActiveLayer.EdgePixels;

            int height = sourceRect.Height;
            int width = sourceRect.Width;
            int xpos, ypos;
            int segIndex = 0;
            Rectangle sourceRectPlusBoundary = new Rectangle(sourceRect.X - placementDelta,
                sourceRect.Y - placementDelta, sourceRect.Width + 2 * placementDelta,
                sourceRect.Height + 2 * placementDelta);
            double angle;
            double angleR;
            float brushLength;
            float brushWidth;
            RotateBilinearAndColor rotationFilterAndColor = new RotateBilinearAndColor(0, Color.White);
            AForge.Imaging.Filters.RotateBilinear rotationFilter = new AForge.Imaging.Filters.RotateBilinear(0);
            rotationFilterAndColor.FillColor = Color.FromArgb(0, 0, 0, 0);
            Segment seg;

            PBR.Filters.StrokeComposite strokeComposite = new PBR.Filters.StrokeComposite();

            // Draw white
            gColor.FillRectangle(new SolidBrush(Color.FromArgb(255, 255, 255, 255)), new Rectangle(0, 0, sourceRect.Width, sourceRect.Height));

            // Draw sourceimage
            gColor.DrawImage(colorImage, new Rectangle(new Point(0, 0), sourceRect.Size),
                sourceRect, GraphicsUnit.Pixel);
            if (generateAll)
            {
                gHeight.FillRectangle(new SolidBrush(Color.White), fullImageRect);
            }


            // Lock the source images
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.ColorImages)
            {
                bU.Lock(ImageLockMode.ReadOnly);
            }
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.EdgesImages)
            {
                bU.Lock(ImageLockMode.ReadOnly);
            }
            anglesImage.Lock(ImageLockMode.ReadOnly);

            // Two pass structure
            // Pass 0 - draw without edges
            // Pass 1 - draw edges only

            // Pass 0
            // Set the appropriate default line length
            for (int i = 0; i < iterations && numTries < maxTries; i++, numTries++)
            {
                // Generate a random stroke location (center of stroke)
                xpos = rand.Next(sourceRect.Left - placementDelta, sourceRect.Right + placementDelta);
                ypos = rand.Next(sourceRect.Top - placementDelta, sourceRect.Bottom + placementDelta);

                // Calculate the corresponding image position
                int imageXPos = xpos;
                int imageYPos = ypos;
                if (xpos < sourceRect.Left)
                {
                    imageXPos = sourceRect.Left;
                }
                else if (xpos >= sourceRect.Right)
                {
                    imageXPos = sourceRect.Right - 1;
                }
                if (ypos < sourceRect.Top)
                {
                    imageYPos = sourceRect.Top;
                }
                else if (ypos >= sourceRect.Bottom)
                {
                    imageYPos = sourceRect.Bottom - 1;
                }

                // Find the segment we are in
                // and use the corresponding kdtrees
                segIndex = objectLabels[imageXPos, imageYPos];
                if (segIndex < 0)
                {
                    i--;
                    continue;
                }
                seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
                vectorsKDTree = seg.vectorsKDTree;
                float segOverlapF = (float)seg.Material.Overlap / 100.0f;

                // set the color and edges images based upon the segment
                // and the image tier
                blurredImage = scene.Painting.ActiveLayer.ImageTier.ColorImages[seg.Material.EdgeLevel];
                edgesImage = scene.Painting.ActiveLayer.ImageTier.EdgesImages[seg.Material.EdgeLevel];

                // Only paint if we could see the stroke
                if ((blurredImage.GetCell(imageXPos, imageYPos, 3) < 10) || (edgesImage.GetCellBW(imageXPos, imageYPos) > seg.Material.EdgeThreshAbs))
                {
                    i--;
                    continue;
                }

                // ToDo: Put Back
                // Get the brush mip
                int brushChoice = rand.Next(0, 7);
                //switch (brushChoice)
                //{
                //    case 0:
                //        colorBrushMip = colorStraightBrushMip;
                //        break;
                //    case 1:
                //        colorBrushMip = colorUpBrushMip;
                //        break;
                //    case 2:
                //        colorBrushMip = colorDownBrushMip;
                //        break;
                //    default:
                //        throw new Exception("Rand should be returning a value between 0 and 2");
                //}


                // Interpolate the correct angle from the placed vectors
                byte angleB;
                int brushIndex = -1;
                radialBasisInterp.InterpAngle(vectorsKDTree, new Point(xpos, ypos), out angleB, out brushIndex);
                angle = angleB / 255.0;
                angleR = angle * Math.PI;
                angle = angle * 180.0;
                colorBrushMip = scene.Painting.Brush.ColorBrushes[brushIndex];
                heightBrushMip = scene.Painting.Brush.HeightBrushes[brushIndex];


                // Calc the jittered stroke length
                float lengthScale = (float)rand.NextDouble();
                lengthScale -= 0.5f;
                lengthScale *= brushDelta * 2;
                lengthScale += 1.0f;
                brushLength = seg.Material.BaseStrokeLength * lengthScale;
                    
                // Find stroke length - strokes shrink near edges
                int distCheck = 1; // Num pixels b/w checks
                float newHalfBrushLength = brushLength / 2.0f;
                float newHalfBrushWidth = brushLength / 4.0f; 
                byte edgeByte;
                float walkX;
                float walkY;

                // Check length in the 0 degree direction
                float xDelta = (float)Math.Cos(angleR) * distCheck;
                float yDelta = -1.0f * (float)Math.Sin(angleR) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushLength; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushLength = (j - distCheck) * segOverlapF;
                        newHalfBrushLength = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushLength *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Check length in the 180 degree direction
                xDelta = (float)Math.Cos(angleR + Math.PI) * distCheck;
                yDelta = -1.0f * (float)Math.Sin(angleR + Math.PI) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushLength; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushLength = (j - distCheck) * segOverlapF;
                        newHalfBrushLength = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushLength *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Check length in the 90 degree direction
                xDelta = (float)Math.Cos(angleR + Math.PI / 2) * distCheck;
                yDelta = -1.0f * (float)Math.Sin(angleR + Math.PI / 2) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushWidth; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushWidth = (j - distCheck) * segOverlapF;
                        newHalfBrushWidth = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushWidth *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Check length in teh 270 degree direction
                xDelta = (float)Math.Cos(angleR - Math.PI / 2) * distCheck;
                yDelta = -1.0f * (float)Math.Sin(angleR - Math.PI / 2) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushWidth; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushWidth = (j - distCheck) * segOverlapF;
                        newHalfBrushWidth = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushWidth *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Assign new brush length
                brushLength = newHalfBrushLength * 2;
                brushWidth = newHalfBrushWidth * 2;

                // Get the correct brush
                colorBrush = colorBrushMip.GetImage((int)brushLength).Bitmap;
                if (generateAll)
                {
                    heightBrush = heightBrushMip.GetImage((int)brushLength).Bitmap;
                }

                float brushAspectRatio = (float)colorBrush.Width / colorBrush.Height;
                float newBrushLength = brushWidth * brushAspectRatio;
                if (newBrushLength < brushLength)
                {
                    brushLength = newBrushLength;
                }
                //brushLength += seg.Overlap;
                if (brushLength < 5/*seg.EdgeStrokeLength*/)
                {
                    //brushLength = seg.EdgeStrokeLength * lengthScale;
                    i--;
                    continue;
                }

                // brushWidthset to represent final brushWidth
                brushWidth = brushLength / brushAspectRatio;

                // Jitter the angle
                float angleScale = (float)rand.NextDouble();
                angleScale -= 0.5f;
                angleScale *= angleDelta * 2;
                angleScale += 1.0f;
                angle = (int)(angle * angleScale);
                angle = angle % 360;
                rotationFilterAndColor.Angle = angle;
                rotationFilter.Angle = angle;
                //angle = ((double)anglesImage.GetCellBW(xpos, ypos)) / 255.0 * Math.PI * 2;

                // Get the stroke color
                byte red = blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.R);
                byte green = blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.G);
                byte blue = blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.B);
                float alphaScale = (float)rand.NextDouble();
                alphaScale -= 0.5f;
                alphaScale *= 2 * alphaDelta;
                alphaScale += 1.0f;
                byte alpha = (byte)(blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.A) * (seg.Material.Opacity / 100.0) * alphaScale);
                //b yte alpha = (byte)(255 - edgesImage.GetCellBW(imageXPos, imageYPos));
                
                // Rotate the stroke
                rotationFilterAndColor.ToColor = Color.FromArgb(alpha, red, green, blue);

                // Shrink the brush in the width only
                AForge.Imaging.Filters.ResizeBilinear resizeBilinear = new AForge.Imaging.Filters.ResizeBilinear((int)brushLength, (int)(brushWidth));
                colorBrush = resizeBilinear.Apply(colorBrush);
                if (generateAll)
                {
                    heightBrush = resizeBilinear.Apply(heightBrush);
                }

                float brushScale = brushLength / colorBrush.Width;
                colorBrush = rotationFilterAndColor.Apply(colorBrush);
                if (generateAll)
                {
                    rotationFilterAndColor.ToColor = Color.FromArgb(255, 255, 255, 255);
                    heightBrush = rotationFilterAndColor.Apply(heightBrush);
                }

                int drawXPos = imageXPos - colorBrush.Width / 2;
                int drawYPos = imageYPos - colorBrush.Height / 2;

                gColor.DrawImage(colorBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, colorBrush.Width, colorBrush.Height);
                //strokeComposite.ApplyInPlace(


                //if (generateAll)
                //{
                //    gHeight.DrawImage(colorBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, colorBrush.Width, colorBrush.Height);
                //}
                if (generateAll)
                {
                    gHeight.DrawImage(heightBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, heightBrush.Width, heightBrush.Height);
                }

                // Store the stroke information
                if (generateAll)
                {
                    Stroke stroke = new Stroke(new Point(xpos, ypos), (float)angle, (int)brushLength,
                        (int)brushWidth, i, rotationFilterAndColor.ToColor, brushIndex);
                    strokeList.Add(stroke);
                    double [] dLoc = new Double[2] {xpos, ypos};
                    strokeKDTree.Insert(dLoc, stroke);
                }

                // Update the progressBar
                int newValue = (int) ((float) i / iterations * 100.0f);
                if (newValue >= scene.ProgressBar.Value + 5)
                {
                    scene.ProgressBar.Value = (int)((float)i / iterations * 100.0f);
                }            
            }

            scene.ProgressBar.Value = 0;

            if (showBorders)
            {
                gColor.DrawImage(scene.Painting.ActiveLayer.OverlayImage,
                    new Rectangle(new Point(0, 0), sourceRect.Size), sourceRect, GraphicsUnit.Pixel);
            }

            //colorImage.Unlock();
            anglesImage.Unlock();
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.ColorImages)
            {
                bU.Unlock();
            }
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.EdgesImages)
            {
                bU.Unlock();
            }

            gColor.Flush();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="paintImage"></param>
        /// <param name="heightImage"></param>
        /// <param name="sourceRect"></param>
        /// <param name="generateAll"></param>
        public void PaintRandomNew(Bitmap paintImage, Bitmap heightImage, Rectangle sourceRect, bool generateAll)
        {
            // Create local references to global resources
            Bitmap colorImage = scene.Painting.ActiveLayer.ColorImage.Bitmap;
            Rectangle fullImageRect = new Rectangle(0, 0, colorImage.Width, colorImage.Height);

            // validate input
            if (generateAll && sourceRect != fullImageRect)
            {
                throw new ArgumentException("generateHeight should be true only when paint is called on the entire image");
            }

            //if ( colorBlurDirty )   // Regenerate blurred color if needed
            //{
            //    GenerateBlurredColor();
            //}
            if (scene.Painting.ActiveLayer.segments.Count == 0)
            {
                SegmentImage();
            }

            // Create local references and 
            // clear the old saved stroke data
            List<Stroke> strokeList = scene.Painting.StrokeList;
            if (generateAll)
            {
                strokeList.Clear();
                scene.Painting.StrokeKDTree = new KDTreeDLL.KDTree(2);
            }
            KDTreeDLL.KDTree strokeKDTree = scene.Painting.StrokeKDTree;

            int iterations = (int)(baseNumIter *
                ((double)sourceRect.Width / scene.Painting.ActiveLayer.ColorImage.Width) *
                ((double)sourceRect.Height / scene.Painting.ActiveLayer.ColorImage.Height));

            int totalArea = colorImage.Width * colorImage.Height;

            ArrayList segmentsArrayList = new ArrayList();
            int avgStrokeWidth = 0;
            foreach (Segment currSeg in scene.Painting.ActiveLayer.segments)
            {
                avgStrokeWidth += (int)(currSeg.Material.BaseStrokeLength * currSeg.Pixels.Count / totalArea);
            }

            // clear the old stroke number data
            // this ensures that we have equal coverage
            float intersectRatio = 1.0f;
            Rectangle segIntersectRect;
            Rectangle sourceRectPlusBoundary = new Rectangle(sourceRect.X - placementDelta,
                sourceRect.Y - placementDelta, sourceRect.Width + 2 * placementDelta,
                sourceRect.Height + 2 * placementDelta);
            int totalStrokesDesired = 0;
            foreach (Segment currSeg in scene.Painting.ActiveLayer.segments)
            {
                currSeg.StrokesDesired = 0;
                currSeg.StrokesPlaced = 0;
                if (sourceRect.IntersectsWith(currSeg.rect))
                {
                    segIntersectRect = new Rectangle(currSeg.rect.X, currSeg.rect.Y, currSeg.rect.Width, currSeg.rect.Height);
                    segIntersectRect.Intersect(sourceRectPlusBoundary);
                    intersectRatio = (float)(segIntersectRect.Width * segIntersectRect.Height) / (currSeg.rect.Width * currSeg.rect.Height);
                    currSeg.StrokesDesired = (int)((baseNumIter * intersectRatio * ((float)avgStrokeWidth / currSeg.Material.BaseStrokeLength) * 
                            ((float)currSeg.Pixels.Count / totalArea)));
                    totalStrokesDesired += currSeg.StrokesDesired;
                    segmentsArrayList.Add(currSeg);
                }
            }

            // Decide the number of iterations

            //int brushLengthThresh = (int)(baseLineLength / 3); // Don't draw strokes with length less than this

            // Reset the progressBar
            scene.ProgressBar.Value = 0;

            Graphics gColor = Graphics.FromImage(paintImage);

            // Draw white
            gColor.FillRectangle(new SolidBrush(Color.FromArgb(255, 255, 255, 255)), new Rectangle(0, 0, sourceRect.Width, sourceRect.Height));

            // Draw sourceimage
            gColor.DrawImage(scene.Painting.ActiveLayer.ImageTier.ColorImages[1].Bitmap, new Rectangle(new Point(0, 0), sourceRect.Size),
                sourceRect, GraphicsUnit.Pixel);
            if (generateAll)
            {
                Graphics gHeight = Graphics.FromImage(heightImage);
                gHeight.FillRectangle(new SolidBrush(Color.White), fullImageRect);
                gHeight.Flush();
            }
            gColor.Flush();

            // Set the compositing Modes
            //gColor.CompositingMode = CompositingMode.SourceOver;
            //gColor.CompositingQuality = compositingQuality;
            //gColor.InterpolationMode = InterpolationMode.Bilinear;

            BitmapUnsafe blurredImage;
            BitmapUnsafe edgesImage;
            BitmapUnsafe anglesImage = scene.Painting.ActiveLayer.AnglesImage;
            int[,] objectLabels = scene.Painting.ActiveLayer.ObjectLabels;
            //BitmapUnsafe colorImage = scene.Painting.ActiveLayer.Color;
            KDTreeDLL.KDTree vectorsKDTree;
            BrushMipMap colorBrushMip;
            BrushMipMap heightBrushMip;
            ArrayList edgePixels = scene.Painting.ActiveLayer.EdgePixels;

            int height = sourceRect.Height;
            int width = sourceRect.Width;
            int xpos = 0;
            int ypos = 0;
            int segIndex = 0;

            double angle;
            double angleR;
            float brushLength;
            float brushWidth;
            RotateBilinearAndColor rotationFilterAndColor = new RotateBilinearAndColor(0, Color.White);
            AForge.Imaging.Filters.RotateBilinear rotationFilter = new AForge.Imaging.Filters.RotateBilinear(0);
            rotationFilterAndColor.FillColor = Color.FromArgb(0, 0, 0, 0);
            Segment seg;
            int pixelIndex = 0;
            Segment.SimplePoint drawPt;

            PBR.Filters.StrokeComposite strokeComposite = new PBR.Filters.StrokeComposite();

            // Draw sourceimage
            //gColor.DrawImage(colorImage, new Rectangle(new Point(0, 0), sourceRect.Size),
            //    sourceRect, GraphicsUnit.Pixel);
            //if (generateAll)
            //{
            //    gHeight.FillRectangle(new SolidBrush(Color.White), fullImageRect);
            //}

            // Lock the source images
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.ColorImages)
            {
                bU.Lock(ImageLockMode.ReadOnly);
            }
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.EdgesImages)
            {
                bU.Lock(ImageLockMode.ReadOnly);
            }
            anglesImage.Lock(ImageLockMode.ReadOnly);

            // Two pass structure
            // Pass 0 - draw without edges
            // Pass 1 - draw edges only


            // Pass 0
            // Set the appropriate default line length
            for (int i = 0; i < iterations && segmentsArrayList.Count > 0; i++)
            {
                // choose a segment at random
                segIndex = rand.Next(0, segmentsArrayList.Count);
                seg = (Segment)segmentsArrayList[segIndex];

                // if this segment is full remove it
                if (seg.StrokesPlaced >= seg.StrokesDesired - 1)
                {
                    segmentsArrayList.Remove(seg);
                    i--;
                    continue;
                }

                // if a full render
                segIntersectRect = new Rectangle(seg.rect.X, seg.rect.Y, seg.rect.Width, seg.rect.Height);
                segIntersectRect.Intersect(sourceRectPlusBoundary);
                // if the segment is entirely contained within sourceRect (fastest approach)
                if (segIntersectRect.Equals(seg.rect))
                {
                    pixelIndex = rand.Next(0, seg.Pixels.Count);
                    drawPt = (Segment.SimplePoint)seg.Pixels[pixelIndex];
                    xpos = drawPt.x;
                    ypos = drawPt.y;
                }
                else // preview window
                {
                    // choose a location within the intersection of the segement rectangle
                    // and the current rectangle
                    /*bool found = false;
                    while (!found)
                    {*/
                        xpos = rand.Next(segIntersectRect.Left, segIntersectRect.Right);
                        ypos = rand.Next(segIntersectRect.Top, segIntersectRect.Bottom);

                        // if an edge continue
                        segIndex = objectLabels[xpos, ypos];
                        if (segIndex < 0)
                        {
                            continue;
                        }
                        seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];

                        // if we chose an seg that is not this seg but still needs
                        // strokes then use it
                        /*seg = (Segment)(scene.Painting.ActiveLayer.segments[segIndex]);
                        if (seg.StrokesPlaced >= seg.StrokesDesired - 1)
                        {
                            break;
                        }
                        found = true;
                    }
                    if (!found)
                    {
                        i--;
                        continue;
                    }*/
                }
                seg.StrokesPlaced++;

                // Calculate the corresponding image position
                int imageXPos = xpos;
                int imageYPos = ypos;
                if (xpos < sourceRect.Left)
                {
                    imageXPos = sourceRect.Left;
                }
                else if (xpos >= sourceRect.Right)
                {
                    imageXPos = sourceRect.Right - 1;
                }
                if (ypos < sourceRect.Top)
                {
                    imageYPos = sourceRect.Top;
                }
                else if (ypos >= sourceRect.Bottom)
                {
                    imageYPos = sourceRect.Bottom - 1;
                }

                vectorsKDTree = seg.vectorsKDTree;
                float segOverlapF = (float)seg.Material.Overlap / 100.0f;

                // set the color and edges images based upon the segment
                // and the image tier
                blurredImage = scene.Painting.ActiveLayer.ImageTier.ColorImages[seg.Material.EdgeLevel];
                edgesImage = scene.Painting.ActiveLayer.ImageTier.EdgesImages[seg.Material.EdgeLevel];

                // Only paint if we could see the stroke
                if ((blurredImage.GetCell(imageXPos, imageYPos, 3) < 10) || (edgesImage.GetCellBW(imageXPos, imageYPos) > seg.Material.EdgeThreshAbs))
                {
                    i--;
                    continue;
                }

                // ToDo: Put Back
                // Get the brush mip
                int brushChoice = rand.Next(0, 7);
                //switch (brushChoice)
                //{
                //    case 0:
                //        colorBrushMip = colorStraightBrushMip;
                //        break;
                //    case 1:
                //        colorBrushMip = colorUpBrushMip;
                //        break;
                //    case 2:
                //        colorBrushMip = colorDownBrushMip;
                //        break;
                //    default:
                //        throw new Exception("Rand should be returning a value between 0 and 2");
                //}


                // Interpolate the correct angle from the placed vectors
                // and get the correct brushes
                byte angleB;
                int brushIndex = -1;
                radialBasisInterp.InterpAngle(vectorsKDTree, new Point(xpos, ypos), out angleB, out brushIndex);
                angle = angleB / 255.0;
                angleR = angle * Math.PI;
                angle = angle * 180.0;
                colorBrushMip = scene.Painting.Brush.ColorBrushes[brushIndex];
                heightBrushMip = scene.Painting.Brush.HeightBrushes[brushIndex];

                // Calc the jittered stroke length
                float lengthScale = (float)rand.NextDouble();
                lengthScale -= 0.5f;
                lengthScale *= brushDelta * 2;
                lengthScale += 1.0f;
                brushLength = seg.Material.BaseStrokeLength * lengthScale;

                // Find stroke length - strokes shrink near edges
                int distCheck = 1; // Num pixels b/w checks
                float newHalfBrushLength = brushLength / 2.0f;
                float newHalfBrushWidth = brushLength / 4.0f;
                byte edgeByte;
                float walkX;
                float walkY;

                // Check length in the 0 degree direction
                float xDelta = (float)Math.Cos(angleR) * distCheck;
                float yDelta = -1.0f * (float)Math.Sin(angleR) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushLength; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushLength = (j - distCheck) * segOverlapF;
                        newHalfBrushLength = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushLength *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Check length in the 180 degree direction
                xDelta = (float)Math.Cos(angleR + Math.PI) * distCheck;
                yDelta = -1.0f * (float)Math.Sin(angleR + Math.PI) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushLength; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushLength = (j - distCheck) * segOverlapF;
                        newHalfBrushLength = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushLength *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Check length in the 90 degree direction
                xDelta = (float)Math.Cos(angleR + Math.PI / 2) * distCheck;
                yDelta = -1.0f * (float)Math.Sin(angleR + Math.PI / 2) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushWidth; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushWidth = (j - distCheck) * segOverlapF;
                        newHalfBrushWidth = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushWidth *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Check length in teh 270 degree direction
                xDelta = (float)Math.Cos(angleR - Math.PI / 2) * distCheck;
                yDelta = -1.0f * (float)Math.Sin(angleR - Math.PI / 2) * distCheck;
                walkX = imageXPos + xDelta;
                walkY = imageYPos + yDelta;
                for (int j = distCheck; j < newHalfBrushWidth; j += distCheck, walkX += xDelta, walkY += yDelta)
                {
                    if (!fullImageRect.Contains((int)walkX, (int)walkY))
                    {
                        break;
                    }
                    edgeByte = edgesImage.GetCellBW((int)walkX, (int)walkY);
                    if (edgeByte > seg.Material.EdgeThreshAbs)
                    {
                        //newHalfBrushWidth = (j - distCheck) * segOverlapF;
                        newHalfBrushWidth = (j + 2) * segOverlapF;
                    }
                    else if (edgeByte > seg.Material.EdgeThreshRel)
                    {
                        newHalfBrushWidth *= (float)seg.Material.EdgeScaleRel / 100.0f;
                    }
                }

                // Assign new brush length
                brushLength = newHalfBrushLength * 2;
                brushWidth = newHalfBrushWidth * 2;

                // Get the correct brush
                colorBrush = colorBrushMip.GetImage((int)brushLength).Bitmap;
                if (generateAll)
                {
                    heightBrush = heightBrushMip.GetImage((int)brushLength).Bitmap;
                }

                float brushAspectRatio = (float)colorBrush.Width / colorBrush.Height;
                float newBrushLength = brushWidth * brushAspectRatio;
                if (newBrushLength < brushLength)
                {
                    brushLength = newBrushLength;
                }
                //brushLength += seg.Overlap;
                if (brushLength < 5/*seg.EdgeStrokeLength*/)
                {
                    //brushLength = seg.EdgeStrokeLength * lengthScale;
                    i--;
                    continue;
                }

                // brushWidthset to represent final brushWidth
                brushWidth = brushLength / brushAspectRatio;

                // Jitter the angle
                float angleScale = (float)rand.NextDouble();
                angleScale -= 0.5f;
                angleScale *= angleDelta * 2;
                angleScale += 1.0f;
                angle = (int)(angle * angleScale);
                angle = angle % 360;
                rotationFilterAndColor.Angle = angle;
                rotationFilter.Angle = angle;
                //angle = ((double)anglesImage.GetCellBW(xpos, ypos)) / 255.0 * Math.PI * 2;

                // Get the stroke color
                byte red = blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.R);
                byte green = blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.G);
                byte blue = blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.B);
                float alphaScale = (float)rand.NextDouble();
            
                alphaScale -= 0.5f;
                alphaScale *= 2 * alphaDelta;
                alphaScale += 1.0f;
                byte alpha = (byte)(blurredImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.A) * (seg.Material.Opacity / 100.0) * alphaScale);
                //b yte alpha = (byte)(255 - edgesImage.GetCellBW(imageXPos, imageYPos));

                // Rotate the stroke
                rotationFilterAndColor.ToColor = Color.FromArgb(alpha, red, green, blue);

                // Shrink the brush in the width only
                AForge.Imaging.Filters.ResizeBilinear resizeBilinear = new AForge.Imaging.Filters.ResizeBilinear((int)brushLength, (int)(brushWidth));
                colorBrush = resizeBilinear.Apply(colorBrush);
                if (generateAll)
                {
                    heightBrush = resizeBilinear.Apply(heightBrush);
                }

                float brushScale = brushLength / colorBrush.Width;
                colorBrush = rotationFilterAndColor.Apply(colorBrush);
                if (generateAll)
                {
                    rotationFilterAndColor.ToColor = Color.FromArgb(255, 255, 255, 255);
                    heightBrush = rotationFilterAndColor.Apply(heightBrush);
                }

                int drawXPos = imageXPos - colorBrush.Width / 2;
                int drawYPos = imageYPos - colorBrush.Height / 2;

                //gColor.DrawImage(colorBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, colorBrush.Width, colorBrush.Height);
                Stroke stroke = new Stroke(new Point(xpos, ypos), (float)angle, (int)brushLength, (int)brushWidth, i,
                    Color.FromArgb(alpha, red, green, blue), brushIndex);
                strokeList.Add(stroke);
                double[] dLoc = new Double[2] { xpos, ypos };
                strokeKDTree.Insert(dLoc, stroke);

                strokeComposite.ApplyInPlace(paintImage, colorBrush, new Rectangle(drawXPos, drawYPos, colorBrush.Width, colorBrush.Height),
                    sourceRect, scene.Painting.ActiveLayer.StrokeArray, stroke);

                //if (generateAll)
                //{
                //    gHeight.DrawImage(colorBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, colorBrush.Width, colorBrush.Height);
                //}
                //if (generateAll)
                //{
                //    gHeight.DrawImage(heightBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, heightBrush.Width, heightBrush.Height);
                //}

                // Store the stroke information

                // Update the progressBar
                int newValue = (int)((float)i / iterations * 100.0f);
                if (newValue >= scene.ProgressBar.Value + 5)
                {
                    scene.ProgressBar.Value = (int)((float)i / iterations * 100.0f);
                }
            }

            scene.ProgressBar.Value = 0;

            /*if (showBorders)
            {
                gColor.DrawImage(scene.Painting.ActiveLayer.OverlayImage,
                    new Rectangle(new Point(0, 0), sourceRect.Size), sourceRect, GraphicsUnit.Pixel);
            }*/

            //colorImage.Unlock();
            anglesImage.Unlock();
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.ColorImages)
            {
                bU.Unlock();
            }
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.EdgesImages)
            {
                bU.Unlock();
            }

            //gColor.Flush();
        }



        /// <summary>
        /// Alternate version of paint - curently not used
        /// </summary>
        /// <param name="gColor"></param>
        /// <param name="sourceRect"></param>
        /// <param name="generateHeight"></param>
        //public void Paint2(Graphics gColor, Rectangle sourceRect, bool generateHeight, CompositingQuality compositingQuality)
        //{
        //    if (edgesDirty)
        //    {
        //        GenerateEdges();
        //    }

        //    if (colorBlurDirty)
        //    {
        //        GenerateBlurredColor();
        //    }

        //    // Decide the number of iterations
        //    int iterations = (int)(baseNumIter *
        //        ((double)sourceRect.Width / scene.Painting.ActiveLayer.Color.Width) *
        //        ((double)sourceRect.Height / scene.Painting.ActiveLayer.Color.Height));

        //    // Set the compositing Modes
        //    gColor.CompositingMode = CompositingMode.SourceOver;
        //    gColor.CompositingQuality = compositingQuality;

        //    // Create local references to global resources
        //    BitmapUnsafe colorImage = scene.Painting.ActiveLayer.BlurredImage;
        //    BitmapUnsafe edgesImage = scene.Painting.ActiveLayer.EdgesImage;
        //    BitmapUnsafe anglesImage = scene.Painting.ActiveLayer.AnglesImage;
        //    KDTreeDLL.KDTree vectorsKDTree = scene.Painting.ActiveLayer.VectorsKDTree;
        //    colorStraightBrushMip = scene.Painting.Brush.ColorStraight;
        //    heightBrushMip = scene.Painting.Brush.Height;

        //    int height = sourceRect.Height;
        //    int width = sourceRect.Width;
        //    colorImage.Lock(ImageLockMode.ReadOnly);
        //    edgesImage.Lock(ImageLockMode.ReadOnly);
        //    anglesImage.Lock(ImageLockMode.ReadOnly);
        //    double angle;

        //    RotateBilinearAndColor rotationFilter = new RotateBilinearAndColor(0, Color.White);
        //    rotationFilter.FillColor = Color.FromArgb(0, 0, 0, 0);

        //    if (!generateHeight)
        //    {
        //        //gColor.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, sourceRect.Width, sourceRect.Height));
        //    }

        //    for (int i = 0; i < iterations; i++)
        //    {
        //        int xpos = rand.Next(sourceRect.Left - placementDelta, sourceRect.Right + placementDelta);
        //        int ypos = rand.Next(sourceRect.Top - placementDelta, sourceRect.Bottom + placementDelta);
        //        byte red;
        //        byte green;
        //        byte blue;

        //        int paintLength = rand.Next(1, 4);
        //        for (int j = 0; j < paintLength; j++, i++)
        //        {
        //            int imageXPos = xpos;
        //            int imageYPos = ypos;
        //            if (xpos < sourceRect.Left)
        //            {
        //                imageXPos = sourceRect.Left;
        //            }
        //            else if (xpos >= sourceRect.Right)
        //            {
        //                imageXPos = sourceRect.Right - 1;
        //            }
        //            if (ypos < sourceRect.Top)
        //            {
        //                imageYPos = sourceRect.Top;
        //            }
        //            else if (ypos >= sourceRect.Bottom)
        //            {
        //                imageYPos = sourceRect.Bottom - 1;
        //            }

        //            //if (edgesImage.GetCellBW(imageXPos, imageYPos) < 70)
        //            //{
        //            //    i--;
        //            //    continue;
        //            //}

        //            angle = radialBasisInterp.Interp(vectorsKDTree, new Point(xpos, ypos));
        //            angle = angle / 255.0 * 360.0;

        //            float angleScale = (float)rand.NextDouble();
        //            angleScale -= 0.5f;
        //            angleScale *= angleDelta * 2;
        //            angleScale += 1.0f;
        //            angle = (int)(angle * angleScale);
        //            angle = angle % 360;

        //            rotationFilter.Angle = angle;
        //            //angle = ((double)anglesImage.GetCellBW(xpos, ypos)) / 255.0 * Math.PI * 2;

        //            float alphaScale = (float)rand.NextDouble();
        //            alphaScale -= 0.5f;
        //            alphaScale *= 2 * alphaDelta;
        //            alphaScale += 1.0f;
        //            byte alpha = (byte)(colorImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.A) * baseAlpha * alphaScale);
        //            //byte alpha = (byte)(255 - edgesImage.GetCellBW(imageXPos, imageYPos));

        //            //if (alpha < 230)
        //            //{
        //            //    alpha = 0;
        //            //}

        //            // note it is blue, green, red, alpha in memory
        //            if (j == 0)
        //            {
        //                red = colorImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.R);
        //                green = colorImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.G);
        //                blue = colorImage.GetCell(imageXPos, imageYPos, AForge.Imaging.RGBA.B);
        //                rotationFilter.ToColor = Color.FromArgb(alpha, red, green, blue);
        //            }


        //            colorBrush = colorStraightBrushMip.GetImage((int)baseLineLength).Bitmap;
        //            heightBrush = heightBrushMip.GetImage((int)baseLineLength).Bitmap;
        //            brushes = rotationFilter.Apply(colorBrush, heightBrush);

        //            //float lengthScale = (float) rand.NextDouble();
        //            //lengthScale -= 0.5f;
        //            //lengthScale *= brushDelta * 2;
        //            //lengthScale += 1.0f;
        //            //float brushSize = lineLength * ((float)brushes[0].Width / colorBrush.Width) * lengthScale;// *(1.0f - (edgesImage.GetCellBW(xpos, ypos) / 255.0f)) + 5.0f;

        //            float brushSize = baseLineLength * ((float)brushes[0].Width / colorBrush.Width);// * (1.0f - (edgesImage.GetCellBW(imageXPos, imageYPos) / 255.0f));

        //            colorBrush = brushes[0];
        //            heightBrush = brushes[1];

        //            int adjXPos = xpos - colorBrush.Width / 2;
        //            int adjYPos = ypos - colorBrush.Height / 2;

        //            gColor.DrawImage(colorBrush, adjXPos - sourceRect.Left, adjYPos - sourceRect.Top, brushSize, brushSize);

        //            double angleR = angle / 180.0 * Math.PI;

        //            xpos = (int)(Math.Cos(angleR) * baseLineLength - 10 + xpos);
        //            ypos = (int)(Math.Sin(angleR) * baseLineLength * -1 - 10 + ypos);                    

        //            //GraphicsPath gp = new GraphicsPath();
        //            //gp.AddBezier(new Point(xpos, ypos), new Point(xpos + 10, ypos), new Point(xpos + 10, ypos + 10), new Point(xpos + 20, ypos + 10));
        //            //gColor.DrawPath(new Pen(new TextureBrush(colorBrush), 5.0f), gp);

        //            //if (generateHeight)
        //            //{
        //            //    gHeight.DrawImage(heightBrush, xpos, ypos, brushSize, brushSize);
        //            //}
        //            //g.DrawLine(pen, row, col, row + x, col + y);
        //            //g.DrawEllipse(pen, row, col, circleRadius, circleRadius);
        //        }
        //    }

        //    anglesImage.Unlock();
        //    colorImage.Unlock();
        //    edgesImage.Unlock();
        //    gColor.Flush();
        //}

        public void PaintSaved(Bitmap paintImage, Bitmap heightImage, Rectangle sourceRect, bool generateAll)
        {
            // Create local references to global resources
            Bitmap colorImage = scene.Painting.ActiveLayer.ColorImage.Bitmap;
            Rectangle fullImageRect = new Rectangle(0, 0, colorImage.Width, colorImage.Height);

            // validate input
            if (generateAll && sourceRect != fullImageRect)
            {
                throw new ArgumentException("generateHeight should be true only when paint is called on the entire image");
            }

            // Reset the prog+ressBar
            scene.ProgressBar.Value = 0;
            int iterations = (int)(baseNumIter *
                ((double)sourceRect.Width / scene.Painting.ActiveLayer.ColorImage.Width) *
                ((double)sourceRect.Height / scene.Painting.ActiveLayer.ColorImage.Height));
            
            // Init the brushes
            int height = sourceRect.Height;
            int width = sourceRect.Width;
            Rectangle sourceRectPlusBoundary = new Rectangle(sourceRect.X - placementDelta,
                sourceRect.Y - placementDelta, sourceRect.Width + 2 * placementDelta,
                sourceRect.Height + 2 * placementDelta);

            // Local variables
            int xpos, ypos;
            double angle;
            float brushLength;
            float brushWidth;
            RotateBilinearAndColor rotationFilterAndColor = new RotateBilinearAndColor(0, Color.White);
            AForge.Imaging.Filters.RotateBilinear rotationFilter = new AForge.Imaging.Filters.RotateBilinear(0);
            rotationFilterAndColor.FillColor = Color.FromArgb(0, 0, 0, 0);
            PBR.Filters.StrokeComposite strokeComposite = new PBR.Filters.StrokeComposite();

            Graphics gColor = Graphics.FromImage(paintImage);

            // Draw white
            gColor.FillRectangle(new SolidBrush(Color.FromArgb(255, 255, 255, 255)), new Rectangle(0, 0, sourceRect.Width, sourceRect.Height));

            // Draw sourceimage
            gColor.DrawImage(scene.Painting.ActiveLayer.ImageTier.ColorImages[1].Bitmap, new Rectangle(new Point(0, 0), sourceRect.Size),
                sourceRect, GraphicsUnit.Pixel);
            if (generateAll)
            {
                Graphics gHeight = Graphics.FromImage(heightImage);
                gHeight.FillRectangle(new SolidBrush(Color.White), fullImageRect);
                gHeight.Flush();
            }
            gColor.Flush();

            int i = 0;
            foreach (Stroke stroke in scene.Painting.StrokeList)
            {
                // Generate a random stroke location (center of stroke)
                xpos = stroke.Loc.X;
                ypos = stroke.Loc.Y;

                if (!sourceRectPlusBoundary.Contains(xpos, ypos))
                {
                    continue;
                }

                // Calculate the corresponding image position
                int imageXPos = xpos;
                int imageYPos = ypos;
                if (xpos < sourceRect.Left)
                {
                    imageXPos = sourceRect.Left;
                }
                else if (xpos >= sourceRect.Right)
                {
                    imageXPos = sourceRect.Right - 1;
                }
                if (ypos < sourceRect.Top)
                {
                    imageYPos = sourceRect.Top;
                }
                else if (ypos >= sourceRect.Bottom)
                {
                    imageYPos = sourceRect.Bottom - 1;
                }

                // assign local references
                angle = stroke.Angle;
                brushLength = stroke.Width;
                brushWidth = stroke.Height;
                
                // get correct brushes
                colorBrush = scene.Painting.Brush.ColorBrushes[stroke.BrushIndex].GetImage((int)brushLength).Bitmap;
                if (generateAll)
                {
                    heightBrush = scene.Painting.Brush.HeightBrushes[stroke.BrushIndex].GetImage((int)brushLength).Bitmap;
                }

                // Rotate the stroke
                rotationFilterAndColor.Angle = angle;
                rotationFilter.Angle = angle;

                // Rotate the stroke
                rotationFilterAndColor.ToColor = stroke.BaseColor;

                // Shrink the brush in the width only
                AForge.Imaging.Filters.ResizeBilinear resizeBilinear = new AForge.Imaging.Filters.ResizeBilinear((int)brushLength, (int)(brushLength * (float)colorBrush.Height / colorBrush.Width));
                colorBrush = resizeBilinear.Apply(colorBrush);
                if (generateAll)
                {
                    heightBrush = resizeBilinear.Apply(heightBrush);
                }

                float brushScale = brushLength / colorBrush.Width;
                colorBrush = rotationFilterAndColor.Apply(colorBrush);
                if (generateAll)
                {
                    heightBrush = rotationFilter.Apply(heightBrush);
                }

                int drawXPos = xpos - colorBrush.Width / 2;
                int drawYPos = ypos - colorBrush.Height / 2;

                strokeComposite.ApplyInPlace(paintImage, colorBrush,
                    new Rectangle(drawXPos, drawYPos, colorBrush.Width, colorBrush.Height),
                    sourceRect, scene.Painting.ActiveLayer.StrokeArray, stroke);

                //gColor.DrawImage(colorBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, brushScale * colorBrush.Width, brushScale * colorBrush.Height);
                if (generateAll)
                {
                    //gHeight.DrawImage(heightBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, brushScale * colorBrush.Width, brushScale * colorBrush.Height);
                }

                // Update the progressBar
                if (generateAll)
                {
                    int newValue = (int)((float)i++ / iterations * 100.0f);
                    if (newValue >= scene.ProgressBar.Value + 5)
                    {
                        scene.ProgressBar.Value = (int)((float)i / iterations * 100.0f);
                    }
                }
            }

            scene.ProgressBar.Value = 0;

            /*if (showBorders)
            {
                gColor.DrawImage(scene.Painting.ActiveLayer.OverlayImage,
                    new Rectangle(new Point(0, 0), sourceRect.Size), sourceRect, GraphicsUnit.Pixel);
            }*/

            //gColor.Flush();
        }


        /// <summary>
        /// Non-random paint
        /// </summary>
        /// <param name="gColor"></param>
        /// <param name="gHeight"></param>
        /// <param name="sourceRect"></param>
        /// <param name="generateAll"></param>
        /// <param name="compositingQuality"></param>
        /// <param name="showBorders"></param>
        //public void PaintSaved(Graphics gColor, Graphics gHeight, Rectangle sourceRect, bool generateAll, CompositingQuality compositingQuality, bool showBorders)
        //{
        //    // Create local references to global resources
        //    Bitmap colorImage = scene.Painting.ActiveLayer.ColorImage.Bitmap;
        //    Rectangle fullImageRect = new Rectangle(0, 0, colorImage.Width, colorImage.Height);

        //    // validate input
        //    if (generateAll && sourceRect != fullImageRect)
        //    {
        //        throw new ArgumentException("generateHeight should be true only when paint is called on the entire image");
        //    }

        //    // Set local references


        //    // Reset the progressBar
        //    scene.ProgressBar.Value = 0;

        //    // Set the compositing Modes
        //    gColor.CompositingMode = CompositingMode.SourceOver;
        //    gColor.CompositingQuality = compositingQuality;
        //    gColor.InterpolationMode = InterpolationMode.Bilinear;

        //    // Init the brushes
        //    BrushMipMap colorBrushMip = scene.Painting.Brush.ColorStraight;
        //    BrushMipMap heightBrushMip = scene.Painting.Brush.Height;
        //    colorStraightBrushMip = scene.Painting.Brush.ColorStraight;
        //    colorUpBrushMip = scene.Painting.Brush.ColorUp;
        //    colorDownBrushMip = scene.Painting.Brush.ColorDown;
        //    heightBrushMip = scene.Painting.Brush.Height;

        //    int height = sourceRect.Height;
        //    int width = sourceRect.Width;
        //    Rectangle sourceRectPlusBoundary = new Rectangle(sourceRect.X - placementDelta,
        //        sourceRect.Y - placementDelta, sourceRect.Width + 2 * placementDelta,
        //        sourceRect.Height + 2 * placementDelta);
            
        //    // Local variables
        //    int xpos, ypos;
        //    double angle;
        //    double angleR;
        //    float brushLength;
        //    float brushWidth;
        //    RotateBilinearAndColor rotationFilterAndColor = new RotateBilinearAndColor(0, Color.White);
        //    AForge.Imaging.Filters.RotateBilinear rotationFilter = new AForge.Imaging.Filters.RotateBilinear(0);
        //    rotationFilterAndColor.FillColor = Color.FromArgb(0, 0, 0, 0);
        //    Segment seg;

        //    // Draw sourceimage
        //    gColor.DrawImage(colorImage, new Rectangle(new Point(0, 0), sourceRect.Size),
        //        sourceRect, GraphicsUnit.Pixel);
        //    if (generateAll)
        //    {
        //        gHeight.FillRectangle(new SolidBrush(Color.White), fullImageRect);
        //    }

        //    foreach(Stroke stroke in scene.Painting.StrokeList)
        //    {
        //        // Generate a random stroke location (center of stroke)
        //        xpos = stroke.Loc.X;
        //        ypos = stroke.Loc.Y;

        //        if (!sourceRectPlusBoundary.Contains(xpos, ypos))
        //        {
        //            continue;
        //        }

        //        // Calculate the corresponding image position
        //        int imageXPos = xpos;
        //        int imageYPos = ypos;
        //        if (xpos < sourceRect.Left)
        //        {
        //            imageXPos = sourceRect.Left;
        //        }
        //        else if (xpos >= sourceRect.Right)
        //        {
        //            imageXPos = sourceRect.Right - 1;
        //        }
        //        if (ypos < sourceRect.Top)
        //        {
        //            imageYPos = sourceRect.Top;
        //        }
        //        else if (ypos >= sourceRect.Bottom)
        //        {
        //            imageYPos = sourceRect.Bottom - 1;
        //        }

        //        // ToDo - save brush number in stroke

        //        angle = stroke.Angle;
        //        brushLength = stroke.Width;
        //        brushWidth = stroke.Height;

        //        // Get the correct brush
        //        colorBrush = colorBrushMip.GetImage((int)brushLength).Bitmap;
        //        if (generateAll)
        //        {
        //            heightBrush = heightBrushMip.GetImage((int)brushLength).Bitmap;
        //        }

        //        // Rotate the stroke
        //        rotationFilterAndColor.Angle = angle;
        //        rotationFilter.Angle = angle;

        //        // Rotate the stroke
        //        rotationFilterAndColor.ToColor = stroke.BaseColor;

        //        // Shrink the brush in the width only
        //        AForge.Imaging.Filters.ResizeBilinear resizeBilinear = new AForge.Imaging.Filters.ResizeBilinear((int)brushLength, (int)(brushLength * (float)colorBrush.Height / colorBrush.Width));
        //        colorBrush = resizeBilinear.Apply(colorBrush);
        //        if (generateAll)
        //        {
        //            heightBrush = resizeBilinear.Apply(heightBrush);
        //        }

        //        float brushScale = brushLength / colorBrush.Width;
        //        colorBrush = rotationFilterAndColor.Apply(colorBrush);
        //        if (generateAll)
        //        {
        //            heightBrush = rotationFilter.Apply(heightBrush);
        //        }

        //        int drawXPos = xpos - colorBrush.Width / 2;
        //        int drawYPos = ypos - colorBrush.Height / 2;

        //        gColor.DrawImage(colorBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, brushScale * colorBrush.Width, brushScale * colorBrush.Height);
        //        if (generateAll)
        //        {
        //            gHeight.DrawImage(heightBrush, drawXPos - sourceRect.Left, drawYPos - sourceRect.Top, brushScale * colorBrush.Width, brushScale * colorBrush.Height);
        //        }
        //    }

        //    //scene.ProgressBar.Value = 0;

        //    if (showBorders)
        //    {
        //        gColor.DrawImage(scene.Painting.ActiveLayer.OverlayImage,
        //            new Rectangle(new Point(0, 0), sourceRect.Size), sourceRect, GraphicsUnit.Pixel);
        //    }

        //    gColor.Flush();
        //}

        #endregion

        /// <summary>
        /// Loaded into the properties window when painting is selected
        /// </summary>
        #region Properties

        public int BaseIterations
        {
            get
            {
                return baseNumIter;
            }
            set
            {
                baseNumIter = value;
            }
        }

        public int EdgeIterations
        {
            get
            {
                return edgeNumIter;
            }
            set
            {
                edgeNumIter = value;
            }
        }

        public float BaseLineLength
        {
            get
            {
                return baseLineLength;
            }
            set
            {
                baseLineLength = value;
            }
        }

        public float EdgeLineLength
        {
            get
            {
                return edgeLineLength;
            }
            set
            {
                edgeLineLength = value;
            }

        }

        public float AngleDelta
        {
            get
            {
                return angleDelta;
            }
            set
            {
                angleDelta = value;
            }
        }

        public float AlphaDelta
        {
            get
            {
                return alphaDelta;
            }
            set
            {
                alphaDelta = value;
            }
        }

        public float BaseAlpha
        {
            get
            {
                return baseAlpha;
            }
            set
            {
                baseAlpha = value;
            }
        }

        public int SobelSize
        {
            get
            {
                return sobelBlurSize;
            }
            set
            {
                sobelBlurSize = value;
                edgesDirty = true;
            }
        }

        public double SobelSigma
        {
            get
            {
                return sobelBlurSigma;
            }
            set
            {
                sobelBlurSigma = value;
                edgesDirty = true;
            }
        }

        public int ColorBlurSize
        {
            get
            {
                return colorBlurSize;
            }
            set
            {
                colorBlurSize = value;
                colorBlurDirty = true;
            }
        }

        public double ColorBlurSigma
        {
            get
            {
                return colorBlurSigma;
            }
            set
            {
                colorBlurSigma = value;
                colorBlurDirty = true;
            }
        }



        #endregion

    }
}
